查看原文
其他

从大端CPU迁移到小端CPU,及字节序的迁移技术

strongerHuang 2021-02-01
关注、星标公众,不错过精彩内容

素材来源:网络

编辑整理:strongerHuang


之前给大家分享过大小端的一些内容,阅读本文之前可以再次回顾一下:

你真的懂CPU大小端模式吗?

大小端格式由编译器还是CPU决定的?


一、回顾字节序

拿数据 0x01020304为例:


在大端CPU中:数据将存储为0x01(address + 0),0x02(address + 1),0x03(address + 2),0x04(address + 3)。


在小端CPU中:数据将存储为0x04(address + 0),0x03(address + 1),0x02(address + 2),0x01(address + 3)。


如果你的程序使用简单的数据结构(例如“ int”和“ short”),则没有什么麻烦。但是,如果数据结构类似于以下示例,则可能会遇到问题。

union { unsigned int dat; unsigned char c[4]; }X;
void foo( ) { int t0; X.dat = 0x01020304; t0 = X.c[0]; ・・・}

在大端 CPU 中编译并执行此代码时, t0”的值为0x01。在小端CPU中, t0”的值为0x04。


那么问题来了:要想使存储顺序从大端,变为小端,怎么办呢?


方法其实有很多种,这里讲讲针对IAR的两种方法:

  • 使用__big_endian关键字。

  • 使用__REV, __REV16, __REVSH, RBIT函数。


二、使用__big_endian关键字

IAR中__big_endian关键字提供了一种方便的方式来将应用程序从big-endian移植到little-endian。


__big_endian关键字用于访问以big-endian字节顺序存储的变量,而与应用程序其余部分使用的字节顺序无关。在ARMv6或更高版本进行编译时,可以使用__big_endian关键字。


只需添加__big_endian关键字即可,如:

____big_endian union { unsigned int dat; unsigned char c[4]; }X;
void foo( ) {int t0;X.dat = 0x01020304;t0 = X.c[0];・・・}

修改后的代码在低位字节CPU中编译和执行,变量“ t0”为0x01。


注意:此关键字不能用于指针。同样,此属性不能在数组上使用。

同时,关键字__big_endian插入REV指令以交换字节数据,REV指令的插入会影响代码大小和执行时间。


关键字具有限制,不能应用于复杂的数据结构,比如以下代码会生成错误:

__big_endianunion { unsigned long dat; unsigned char c[4]; struct { unsigned long a0: 1; unsigned long a1: 1; unsigned long a2: 2; unsigned long a3: 4; unsigned long a4: 8; unsigned long a5: 16; }s;} f1_dat2;


三、使用__REV, __REV16, __REVSH, RBIT函数

大端和小端之间的字节顺序差异只是顺序,因此我们需要做的是更改字节顺序,我们再次以变量0x01020304为例:


我们可以通过代码实现交换功能,比如:

typedef unsigned long uint32_t;uint32_t bswap_32(uint32_t x) {  uint32_t t = x;  uint32_t s;  s = ( (((uint32_t)(t) & (uint32_t)0x000000ffUL) << 24) |    (((uint32_t)(t) & (uint32_t)0x0000ff00UL) << 8) |      (((uint32_t)(t) & (uint32_t)0x00ff0000UL) >> 8) | (((uint32_t)(t) & (uint32_t)0xff000000UL) >> 24) ); return s; }

通过这种方式实现,将导致消耗更多时间和代码大小。

在C代码中,我们通常编写内联汇编代码实现交换。IAR有种内部函数可以实现该功能。

比如下面交换功能:


代码如下:
#include <intrinsics.h>void x1( void ) {s2 = __REV(s1);s3 = __REV16(s1);s4 = __REVSH(s1);}

当然,具体的使用以及细节内容,需要看查看官方说明。


免责声明:本文素材来源网络,版权归原作者所有。如涉及作品版权问题,请与我联系删除。
推荐阅读:
IAR进军Linux,支持在Linux搭建编译环境
Linux 为何会流行?它和普通的RTOS有何区别?
Windows Terminal 1.0 和 Linux 版有啥区别

关注微信公众号『strongerHuang』,后台回复“1024”查看更多内容,回复“加群”按规则加入技术交流群。


长按前往图中包含的公众号关注

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存